from typing import Optional

from PySide6.QtCore import QRectF, QPointF
from PySide6.QtGui import QColor, QPen, QPainter, QPainterPath, Qt
from PySide6.QtWidgets import QGraphicsPathItem, QStyleOptionGraphicsItem, QWidget, QGraphicsDropShadowEffect


class NodeEdge(QGraphicsPathItem):

    def __init__(self, source_port, des_port, scene, parent=None):
        super().__init__(parent)

        self._source_port = source_port  # 起始插槽
        self._des_port = des_port  # 终止插槽
        self._scene = scene
        # 设置边的颜色
        self.edge_color = self._source_port.get_port_color()  # 边的颜色为起始插槽的颜色
        self._pen_default = QPen(self.edge_color)  # 画笔
        self._pen_default.setWidthF(4)
        self.setZValue(-1)  # 设置Z值，使得边在节点的下面显示
        # 选中投影
        self._shadow = QGraphicsDropShadowEffect()  # 被选中时的投影，选中时增加阴影效果
        self._shadow.setOffset(0, 0)
        self._shadow.setBlurRadius(20)
        self._shadow_color = QColor(Qt.GlobalColor.yellow)
        # 设置边的标志
        self.setFlags(QGraphicsPathItem.GraphicsItemFlag.ItemIsSelectable)  # 设置边可选中
        self.add_to_scene()  # 添加到场景中

    def add_to_scene(self):
        """添加到场景中"""
        self._scene.addItem(self)
        self._source_port.add_edge(self, self._des_port)  # 添加到起始插槽的边列表
        self._des_port.add_edge(self, self._source_port)  # 添加到终止插槽的边列表

    def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem, widget: Optional[QWidget] = None):
        """绘制边"""
        self.update_edge_path()
        painter.setPen(self._pen_default)
        painter.setBrush(Qt.BrushStyle.NoBrush)
        painter.drawPath(self.path())  # 画路径
        if self.isSelected():  # 如果边被选中，设置阴影颜色为预定义的颜色
            self._shadow.setColor(self._shadow_color)
            self.setGraphicsEffect(self._shadow)
        else:
            self._shadow.setColor(QColor('#00000000'))
            self.setGraphicsEffect(self._shadow)

    def update_edge_path(self):
        """更新边的路径"""
        # 获取起始和终止插槽的位置
        source_pos = self._source_port.get_port_pos()
        des_pos = self._des_port.get_port_pos()
        # 创建路径并设置起始点
        path = QPainterPath(source_pos)
        # 计算 X 轴和 Y 轴的差值
        x_width = source_pos.x() - des_pos.x() or 0.01  # 防止 x_width 为 0
        y_height = abs(source_pos.y() - des_pos.y())
        # 计算并调整切线长度
        tangent = min(y_height * 0.5, 150) if x_width > 0 else min(y_height * 0.5, 150)
        # 创建贝塞尔曲线
        path.cubicTo(
            QPointF(source_pos.x() + tangent, source_pos.y()),
            QPointF(des_pos.x() - tangent, des_pos.y()),
            des_pos
        )
        # 设置路径
        self.setPath(path)


class DraggingEdge(QGraphicsPathItem):

    def __init__(
            self,
            source_pos,
            des_pos,
            port_color,
            drag_from_source: bool,
            scene,
            parent=None
    ):
        super().__init__(parent)

        self._source_pos = source_pos  # 起始位置
        self._des_pos = des_pos  # 终止位置
        self._scene = scene
        self._drag_from_source = drag_from_source  # 是否从起始位置拖动

        # 设置边的颜色
        self._edge_color = port_color  # 边的颜色为起始插槽的颜色
        self._pen_default = QPen(QColor(self._edge_color))
        self._pen_default.setWidthF(4)
        self.setZValue(-1)
        self.setFlags(QGraphicsPathItem.GraphicsItemFlag.ItemIsSelectable)
        self.add_to_scene()

    def add_to_scene(self):
        self._scene.addItem(self)

    def paint(self, painter: QPainter, option: QStyleOptionGraphicsItem, widget: Optional[QWidget] = None):
        self.update_edge_path()
        painter.setPen(self._pen_default)
        painter.setBrush(Qt.BrushStyle.NoBrush)
        painter.drawPath(self.path())  # 画路径

    def update_edge_path(self):
        """更新边的路径"""
        # 获取起始和终止插槽的位置
        source_pos = self._source_pos
        des_pos = self._des_pos
        # 创建路径并设置起始点
        path = QPainterPath(source_pos)
        # 计算 X 轴和 Y 轴的差值
        x_width = source_pos.x() - des_pos.x() or 0.01  # 防止 x_width 为 0
        y_height = abs(source_pos.y() - des_pos.y())
        # 计算并调整切线长度
        tangent = min(y_height * 0.5, 150) if x_width > 0 else min(y_height * 0.5, 150)
        # 创建贝塞尔曲线
        path.cubicTo(
            QPointF(source_pos.x() + tangent, source_pos.y()),
            QPointF(des_pos.x() - tangent, des_pos.y()),
            des_pos
        )
        # 设置路径
        self.setPath(path)

    # def update_edge_path(self):
    #     source_pos = self._source_pos
    #     des_pos = self._des_pos
    #     if source_pos is None or des_pos is None:
    #         return
    #     path = QPainterPath(source_pos)
    #     xwidth = source_pos.x() - des_pos.x()
    #     xwidth = xwidth + 0.01 if xwidth == 0 else xwidth
    #     yheight = abs(source_pos.y() - des_pos.y())
    #     tangent = float(yheight) / xwidth * 0.5
    #     # tangent = tangent if tangent < 1 else 1
    #     tangent *= xwidth
    #     if xwidth > 0:
    #         if xwidth > 150:
    #             xwidth = 150
    #         tangent += xwidth
    #     else:
    #         if tangent > 150:
    #             tangent = 150
    #     path.cubicTo(
    #         QPointF(source_pos.x() + tangent, source_pos.y()),
    #         QPointF(des_pos.x() - tangent, des_pos.y()), des_pos
    #     )
    #
    #     self.setPath(path)

    def update_positions(self, position):
        """更新为鼠标的位置"""
        if self._drag_from_source:
            self._des_pos = position
        else:
            self._source_pos = position
        self.update()
